//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------
/*
	EventTransmitter - transmit events from one thread to other thread.
*/

#pragma once

#include "Base/Events/EventSystem.h"
#include "Base/Parallel/ParallelThreadManager.h"

namespace Engine
{
namespace Base
{

	class EventTransmitter;
	SHARED_POINTER( EventTransmitter );



	//
	// Event Transmitter
	//

	class EventTransmitter : public EventSystem
	{
	// types
	private:
		typedef TBitfield< ESystemEvent::_COUNT >	bitfield;


	// variables
	private:
		EventSystemPtr		_eventSys;
		bitfield			_attached;


	// methods
	public:
		EventTransmitter (const EventSystemPtr &sourceEventSys, const EngineSubSystemsRef ess) :
			EventSystem(ess), _eventSys(sourceEventSys)
		{
			ASSERT( sourceEventSys.IsNotNull() );
		}


		~EventTransmitter ()
		{
			Clear();
			Wait();

			ASSERT_EXT( _attached.IsZero(), "some event listeners are not detached!" );
		}


		void AttachEventListener (const EventCallback_t &callback, ESystemEvent::type eventType) override
		{
			if ( not _attached.Get( eventType ) )
			{
				_attached.Set( eventType );

				ParallelOp	op;
				FunctionBuilder::Create( op.func, EventTransmitterPtr(this), &EventTransmitter::_AttachEL, eventType );

				_eventSys->GetSubSystems()->GetCurrentThread()->Push( ToRValueRef( op ) );
			}

			EventSystem::AttachEventListener( callback, eventType );
		}


		void DetachEventListener (const EventCallback_t &callback, ESystemEvent::type eventType) override
		{
			EventSystem::DetachEventListener( callback, eventType );

			if ( _eventListeners[eventType].Empty() )
			{
				_attached.Reset( eventType );
				
				ParallelOp	op;
				FunctionBuilder::Create( op.func, EventTransmitterPtr(this), &EventTransmitter::_DetachEL, eventType );
				
				_eventSys->GetSubSystems()->GetCurrentThread()->Push( ToRValueRef( op ) );
			}
		}
		

		void Clear () override
		{
			FOR( i, _attached )
			{
				if ( _attached.Get( i ) )
				{
					_attached.Reset( i );
					
					ParallelOp	op;
					FunctionBuilder::Create( op.func, EventTransmitterPtr(this), &EventTransmitter::_DetachEL, ESystemEvent::type(i) );
					
					_eventSys->GetSubSystems()->GetCurrentThread()->Push( ToRValueRef( op ) );
				}
			}

			EventSystem::Clear();
		}
		

		void Wait () override
		{
			ParallelThreadManager::Iterate( ESS()->GetCurrentThread().ptr(), _eventSys->GetSubSystems()->GetCurrentThread().ptr() );
		}


		static EventTransmitterPtr	New (const EventSystemPtr &sourceEventSys, const EngineSubSystemsRef ess)
		{
			return BaseObject::_New( new EventTransmitter( sourceEventSys, ess ) );
		}
		

	private:
		void _Transmit (const SysEvent &ev)
		{
			ParallelOp	op;
			FunctionBuilder::Create( op.func, EventTransmitterPtr(this), &EventTransmitter::Send, ev );

			ESS()->GetCurrentThread()->Push( ToRValueRef( op ) );
		}


		void _AttachEL (ESystemEvent::type eventType)
		{
			_eventSys->AttachEventListener(
				DelegateBuilder::Create( EventTransmitterPtr(this), &EventTransmitter::_Transmit ), eventType );
		}


		void _DetachEL (ESystemEvent::type eventType)
		{
			_eventSys->DetachEventListener(
				DelegateBuilder::Create( EventTransmitterPtr(this), &EventTransmitter::_Transmit ), eventType );
		}
	};


}	// Base
}	// Engine